
/******************************************************************************
* $Id$
* Copyright (C) 2003-2007 Kepler Project.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/

/***************************************************************************
*
* $ED
*    This module is the implementation of luajava's dynamic library.
*    In this module lua's functions are exported to be used in java by jni,
*    and also the functions that will be used and exported to lua so that
*    Java Objects' functions can be called.
*
*****************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include "luajava.h"
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "log.h"


/* Constant that is used to index the JNI Environment */
#define LUAJAVAJNIENVTAG      "__JNIEnv"
/* Defines whether the metatable is of a java Object */
#define LUAJAVAOBJECTIND      "__IsJavaObject"
/* Defines the lua State Index Property Name */
#define LUAJAVASTATEINDEX     "LuaJavaStateIndex"
/* Index metamethod name */
#define LUAINDEXMETAMETHODTAG "__index"
/* New index metamethod name */
#define LUANEWINDEXMETAMETHODTAG "__newindex"
/* Garbage collector metamethod name */
#define LUAGCMETAMETHODTAG    "__gc"
/* to string metamethod name */
#define LUATOSTRINGMETAMETHODTAG "__tostring"
/* Call metamethod name */
#define LUACALLMETAMETHODTAG  "__call"
/* Constant that defines where in the metatable should I place the function name */
#define LUAJAVAOBJFUNCCALLED  "__FunctionCalled"
/* Constant that define the base metatable in register index table */
#define LUABASEMETATABLE  "__BaseMetatable"



static jclass    throwable_class      = NULL;
static jmethodID get_message_method   = NULL;
static jclass    java_function_class  = NULL;
static jmethodID java_function_method = NULL;
static jclass    luajava_api_class    = NULL;
static jclass    java_lang_class      = NULL;

static jmethodID luajava_getFieldValue_method    = NULL;
static jmethodID luajava_setFieldValue_method    = NULL;
static jmethodID luajava_callMethod_method       = NULL;
static jmethodID luajava_getFieldOrMethod_method = NULL;
static jmethodID luajava_getWritableField_method = NULL;
static jmethodID luajava_createProxyObject_interface = NULL;
static jmethodID luajava_arrayIndex              = NULL;
static jmethodID luajava_arrayNewIndex           = NULL;
static jmethodID luajava_newArray                = NULL;
static jmethodID luajava_javaNew                 = NULL;
static jmethodID java_lang_Object_hashCode       = NULL;
static jmethodID java_lang_Object_toString       = NULL;
static jmethodID java_lang_Class_getName         = NULL;
static jmethodID java_lang_Class_forName         = NULL;
static jmethodID java_lang_Class_isArray         = NULL;
static jclass    java_lang_reflect_Field         = NULL;

#define CHECK(L, express, fmt, ...)  do {if (!(express)) luaL_error (L, fmt, #__VA_ARGS__); }while(0)


#define OCCUR_EXCEPTION(env) (((*(env))->ExceptionOccurred(env)) != NULL)

#define PROCESS_EXCEPTION(env, L)  process_java_exception(env, L)

static void process_java_exception (JNIEnv * env, lua_State *L) {
    jthrowable exp = (*env)->ExceptionOccurred (env);
    if  (exp != NULL) {
        jobject jstr;
        const char* cstr;
        (*env)->ExceptionClear (env);
        jstr = (*env)->CallObjectMethod (env, exp, get_message_method);
        
        if (jstr == NULL) {
            jstr = (*env)->CallObjectMethod (env, exp, java_lang_Object_toString);
        }
        if (jstr != NULL) {
            cstr = (*env)->GetStringUTFChars (env, jstr, NULL);
            lua_pushstring (L, cstr);
            (*env)->ReleaseStringUTFChars (env, jstr, cstr);
            (*env)->DeleteLocalRef (env, jstr);
        } else {
            lua_pushstring (L, "Unknown java exception");
        }
        lua_error (L);
    }
}


static jclass getClassFromUpvalue (lua_State *L)
{
   jclass class = NULL;
   lua_rawgeti (L, lua_upvalueindex(1), 0);
   if (lua_isuserdata(L, -1)) {
      class = (jclass)lua_touserdata(L, -1);
   }
   lua_remove(L, -1);
   return class;
}

static jobject getObject(lua_State *L, int top) {
    if (!lua_isuserdata (L, top)) {
        return 0;
    }
    jobject * pobj = (jobject*)lua_touserdata(L, top);
    return pobj ? *pobj : 0;
}

static jobject getObjectOrClass(lua_State *L, int top) {
    jobject obj = getObject(L, top);
    if (obj) {
        return obj;
    }
    if (lua_istable (L, top)) {
        lua_rawgeti(L, top, 0);
        if (lua_isuserdata(L, -1)) {
            obj = (jobject)lua_touserdata(L, -1);
        }
        lua_pop(L, 1);
    }
    return obj;
}

static int getObjectHashCode(JNIEnv *env, jobject obj)
{
    return (*env)->CallIntMethod (env, obj, java_lang_Object_hashCode);
}

static jclass classForName(JNIEnv *env, const char* className) {
    jstring jstr;
    jclass  clazz = NULL;
    jstr = (*env)->NewStringUTF (env, className);
    clazz = (*env)->CallStaticObjectMethod( env, java_lang_class, java_lang_Class_forName, jstr);
    (*env)->DeleteLocalRef(env, jstr);
    return clazz;
}

static int javaObjectIsArray(JNIEnv *env, jobject obj)
{
   return (*env)->CallBooleanMethod (env, (*env)->GetObjectClass(env, obj), java_lang_Class_isArray);
}

static int javaClassIsArray(JNIEnv*env, jclass clazz)
{
   return (*env)->CallBooleanMethod (env, clazz, java_lang_Class_isArray);
}

/******************************************************************
* 
* Function : getJavaStateIndex
*****/
lua_Number getJavaStateIndex (lua_State *L)
{
   lua_Number stateIndex;

   lua_pushstring (L, LUAJAVASTATEINDEX);
   lua_rawget (L, LUA_REGISTRYINDEX);


   CHECK(L, lua_isnumber( L , -1 ), "Impossible to identify luaState id." );

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   return stateIndex;
}


/***************************************************************************
*
* $FC Function objectIndex
* 
* $ED Description
*    Function to be called by the metamethod __index of the java object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int objectIndex( lua_State * L );


/***************************************************************************
*
* $FC Function objectIndexReturn
* 
* $ED Description
*    Function returned by the metamethod __index of a java Object. It is 
*    the actual function that is going to call the java method.
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int objectIndexReturn( lua_State * L );


/***************************************************************************
*
* $FC Function objectNewIndex
* 
* $ED Description
*    Function to be called by the metamethod __newindex of the java object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int objectNewIndex( lua_State * L );


/***************************************************************************
*
* $FC Function classIndex
* 
* $ED Description
*    Function to be called by the metamethod __index of the java class
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int classIndex( lua_State * L );


/***************************************************************************
*
* $FC Function arrayIndex
* 
* $ED Description
*    Function to be called by the metamethod __index of a java array
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int arrayIndex( lua_State * L );
   

/***************************************************************************
*
* $FC Function arrayNewIndex
* 
* $ED Description
*    Function to be called by the metamethod __newindex of a java array
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int arrayNewIndex( lua_State * L );


/***************************************************************************
*
* $FC Function GC
* 
* $ED Description
*    Function to be called by the metamethod __gc of the java object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int gc( lua_State * L );


/***************************************************************************
*
* $FC Function javaBindClass
* 
* $ED Description
*    Implementation of lua function luajava.BindClass
* 
* $EP Function Parameters
*    $P L - lua State
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int javaBindClass( lua_State * L );

/***************************************************************************
*
* $FC Function createProxy
* 
* $ED Description
*    Implementation of lua function luajava.createProxy.
*    Transform a lua table into a java class that implements a list 
*  of interfaces
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int createProxy( lua_State * L );

/***************************************************************************
*
* $FC Function javaNew
* 
* $ED Description
*    Implementation of lua function luajava.new
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int javaNew( lua_State * L );


/***************************************************************************
*
* $FC Function javaNewInstance
* 
* $ED Description
*    Implementation of lua function luajava.newInstance
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int javaNewInstance( lua_State * L );


/***************************************************************************
*
* $FC Function javaLoadLib
* 
* $ED Description
*    Implementation of lua function luajava.loadLib
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int javaLoadLib( lua_State * L );


/***************************************************************************
*
* $FC pushJavaObject
* 
* $ED Description
*    Function to create a lua proxy to a java object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P javaObject - Java Object to be pushed on the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int pushJavaObject( lua_State * L , jobject javaObject );


/***************************************************************************
*
* $FC pushJavaArray
* 
* $ED Description
*    Function to create a lua proxy to a java array
* 
* $EP Function Parameters
*    $P L - lua State
*    $P javaObject - Java array to be pushed on the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int pushJavaArray( lua_State * L , jobject javaObject );


/***************************************************************************
*
* $FC pushJavaClass
* 
* $ED Description
*    Function to create a lua proxy to a java class
* 
* $EP Function Parameters
*    $P L - lua State
*    $P javaObject - Java Class to be pushed on the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function
* 
*$. **********************************************************************/

   static int pushJavaClass( lua_State * L , jobject javaObject );


/***************************************************************************
*
* $FC isJavaObject
* 
* $ED Description
*    Returns 1 is given index represents a java object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P idx - index on the stack
* 
* $FV Returned Value
*    int - Boolean.
* 
*$. **********************************************************************/

   static int isJavaObject( lua_State * L , int idx );


/***************************************************************************
*
* $FC getStateFromCPtr
* 
* $ED Description
*    Returns the lua_State from the CPtr Java Object
* 
* $EP Function Parameters
*    $P L - lua State
*    $P cptr - CPtr object
* 
* $FV Returned Value
*    int - Number of values to be returned by the function.
* 
*$. **********************************************************************/

   static lua_State * getStateFromCPtr( JNIEnv * env , jobject cptr );


/***************************************************************************
*
* $FC luaJavaFunctionCall
* 
* $ED Description
*    function called by metamethod __call of instances of JavaFunctionWrapper
* 
* $EP Function Parameters
*    $P L - lua State
*    $P Stack - Parameters will be received by the stack
* 
* $FV Returned Value
*    int - Number of values to be returned by the function.
* 
*$. **********************************************************************/

   static int luaJavaFunctionCall( lua_State * L );


/***************************************************************************
*
* $FC pushJNIEnv
* 
* $ED Description
*    function that pushes the jni environment into the lua state
* 
* $EP Function Parameters
*    $P env - java environment
*    $P L - lua State
* 
* $FV Returned Value
*    void
* 
*$. **********************************************************************/

   static void pushJNIEnv( JNIEnv * env , lua_State * L );


   /***************************************************************************
*
* $FC getEnvFromState
* 
* $ED Description
*    auxiliary function to get the JNIEnv from the lua state
* 
* $EP Function Parameters
*    $P L - lua State
* 
* $FV Returned Value
*    JNIEnv * - JNI environment
* 
*$. **********************************************************************/

   static JNIEnv * getEnvFromState( lua_State * L );


   /*********************************************************************
* $FC createBaseMetatable
*
* $ED Description
*     create a base metatable as the all class matable's metatable
*
* $EP Function Parameters
*    $P L - lua State
*
* $FV Returned Value
*    void
*
*$. *******************************************************************/
    static void createBaseMetatable( lua_State * L );

    /***************************************************************
* $FC createClassMetatable
*
* $ED Description
*    create a specail metatable for class
*
* $EP Function Parameters
*    $P L - lua State
* 
* $FV Returned Value
*    void
*  
*$. ****************************************************************/
   

/********************* Implementations ***************************/

/***************************************************************************
*
*  Function: objectIndex
*  ****/

int objectIndex( lua_State * L )
{
   lua_Number stateIndex;
   const char * key;
   jmethodID method;
   jint checkField;
   jobject * obj;
   jstring str;
   jthrowable exp;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   if ( !lua_isstring( L , -1 ) )
   {
      lua_pushstring( L , "Invalid object index. Must be string." );
      lua_error( L );
   }

   key = lua_tostring( L , -1 );

   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid Java Object." );
      lua_error( L );
   }

   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   obj = ( jobject * ) lua_touserdata( L , 1 );

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "checkField" ,
                                             "(ILjava/lang/Object;Ljava/lang/String;)I" );

   str = ( *javaEnv )->NewStringUTF( javaEnv , key );

   checkField = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method ,
                                                   (jint)stateIndex , *obj , str );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , str );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , str );

   if ( checkField != 0 )
   {
      return checkField;
   }

   lua_getmetatable( L , 1 );

   if ( !lua_istable( L , -1 ) )
   {
      lua_pushstring( L , "Invalid MetaTable." );
      lua_error( L );
   }

   lua_pushstring( L , LUAJAVAOBJFUNCCALLED );
   lua_pushstring( L , key );
   lua_rawset( L , -3 );

   lua_pop( L , 1 );

   lua_pushcfunction( L , &objectIndexReturn );

   return 1;
}


/***************************************************************************
*
*  Function: objectIndexReturn
*  ****/

int objectIndexReturn( lua_State * L )
{
   lua_Number stateIndex;
   jobject * pObject;
   jmethodID method;
   jthrowable exp;
   const char * methodName;
   jint ret;
   jstring str;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   /* Checks if is a valid java object */
   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid OO function call." );
      lua_error( L );
   }

   lua_getmetatable( L , 1 );
   if ( lua_type( L , -1 ) == LUA_TNIL )
   {
      lua_pushstring( L , "Not a valid java Object." );
      lua_error( L );
   }

   /* Gets the method Name */
   lua_pushstring( L , LUAJAVAOBJFUNCCALLED );
   lua_rawget( L , -2 );
   if ( lua_type( L , -1 ) == LUA_TNIL )
   {
      lua_pushstring( L , "Not a OO function call." );
      lua_error( L );
   }
   methodName = lua_tostring( L , -1 );

   lua_pop( L , 2 );

   /* Gets the object reference */
   pObject = ( jobject* ) lua_touserdata( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   /* Gets method */
   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "objectIndex" ,
                                             "(ILjava/lang/Object;Ljava/lang/String;)I" );

   str = ( *javaEnv )->NewStringUTF( javaEnv , methodName );

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method , (jint)stateIndex , 
                                            *pObject , str );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , str );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , str );

   /* pushes new object into lua stack */
   return ret;
}


/***************************************************************************
*
*  Function: objectNewIndex
*  ****/

int objectNewIndex( lua_State * L  )
{
   lua_Number stateIndex;
   jobject * obj;
   jmethodID method;
   const char * fieldName;
   jstring str;
   jint ret;
   jthrowable exp;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid java class." );
      lua_error( L );
   }

   /* Gets the field Name */

   if ( !lua_isstring( L , 2 ) )
   {
      lua_pushstring( L , "Not a valid field call." );
      lua_error( L );
   }

   fieldName = lua_tostring( L , 2 );

   /* Gets the object reference */
   obj = ( jobject* ) lua_touserdata( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "objectNewIndex" ,
                                             "(ILjava/lang/Object;Ljava/lang/String;)I" );

   str = ( *javaEnv )->NewStringUTF( javaEnv , fieldName );

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , 
                                            *obj , str );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , str );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , str );


   return ret;
}


/***************************************************************************
*
*  Function: classIndex
*  ****/

int classIndex( lua_State * L )
{
   lua_Number stateIndex;
   jobject * obj;
   jmethodID method;
   const char * fieldName;
   jstring str;
   jint ret;
   jthrowable exp;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid java class." );
      lua_error( L );
   }

   /* Gets the field Name */

   if ( !lua_isstring( L , 2 ) )
   {
      lua_pushstring( L , "Not a valid field call." );
      lua_error( L );
   }

   fieldName = lua_tostring( L , 2 );

   /* Gets the object reference */
   obj = ( jobject* ) lua_touserdata( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "classIndex" ,
                                             "(ILjava/lang/Class;Ljava/lang/String;)I" );

   str = ( *javaEnv )->NewStringUTF( javaEnv , fieldName );

   /* Return 1 for field, 2 for method or 0 for error */
   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , 
                                            *obj , str );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , str );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , str );

   if ( ret == 0 )
   {
      lua_pushstring( L , "Name is not a static field or function." );
      lua_error( L );
   }

   if ( ret == 2 )
   {
      lua_getmetatable( L , 1 );
      lua_pushstring( L , LUAJAVAOBJFUNCCALLED );
      lua_pushstring( L , fieldName );
      lua_rawset( L , -3 );

      lua_pop( L , 1 );

      lua_pushcfunction( L , &objectIndexReturn );

      return 1;
   }

   return ret;
}


/***************************************************************************
*
*  Function: arrayIndex
*  ****/

int arrayIndex( lua_State * L )
{
   lua_Number stateIndex;
   lua_Integer key;
   jmethodID method;
   jint ret;
   jobject * obj;
   jthrowable exp;
   JNIEnv * javaEnv;

	/* Can index as number or string */
   if ( !lua_isnumber( L , -1 ) && !lua_isstring( L , -1 ) )
   {
      lua_pushstring( L , "Invalid object index. Must be integer or string." );
      lua_error( L );
   }

	/* Important! If the index is not a number, behave as normal Java object */
	if ( !lua_isnumber( L , -1 ) )
	{
		return objectIndex( L );
	}

	/* Index is number */

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

	// Array index
   key = lua_tointeger( L , -1 );

   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid Java Object." );
      lua_error( L );
   }

   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   obj = getObjectOrClass( L , 1 );

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "arrayIndex" ,
                                             "(ILjava/lang/Object;I)I" );

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method ,
                                                   (jint)stateIndex , *obj , (jlong)key );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   return ret;
}


/***************************************************************************
*
*  Function: arrayNewIndex
*  ****/

int arrayNewIndex( lua_State * L )
{
   lua_Number stateIndex;
   jobject * obj;
   jmethodID method;
   lua_Integer key;
   jint ret;
   jthrowable exp;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a valid java class." );
      lua_error( L );
   }

   /* Gets the field Name */

   if ( !lua_isnumber( L , 2 ) )
   {
      lua_pushstring( L , "Not a valid array index." );
      lua_error( L );
   }

   key = lua_tointeger( L , 2 );

   /* Gets the object reference */
   obj = getObjectOrClass( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "arrayNewIndex" ,
                                             "(ILjava/lang/Object;I)I" );

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , 
                                            *obj , (jint)key );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }


   return ret;
}


/***************************************************************************
*
*  Function: gc
*  ****/

int gc( lua_State * L )
{
   jobject * pObj;
   JNIEnv * javaEnv;

   if ( !isJavaObject( L , 1 ) )
   {
      return 0;
   }

   pObj = getObjectOrClass( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   ( *javaEnv )->DeleteGlobalRef( javaEnv , *pObj );

   return 0;
}


/***************************************************************************
*
*  Function: javaBindClass
*  ****/

int javaBindClass( lua_State * L )
{
   int top;
   jmethodID method;
   const char * className;
   jstring javaClassName;
   jobject classInstance;
   jthrowable exp;
   JNIEnv * javaEnv;

   top = lua_gettop( L );

   if ( top != 1 )
   {
      luaL_error( L , "Error. Function javaBindClass received %d arguments, expected 1." , top );
   }

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   CHECK(L, javaEnv != NULL , "Invalid JNI Environment." );

   /* get the string parameter */
   CHECK(L, lua_isstring( L , 1 ),  "Invalid parameter type. String expected." );
   className = lua_tostring( L , 1 );

#if 0
   method = ( *javaEnv )->GetStaticMethodID( javaEnv , java_lang_class , "forName" , 
                                             "(Ljava/lang/String;)Ljava/lang/Class;" );

   javaClassName = ( *javaEnv )->NewStringUTF( javaEnv , className );

   classInstance = ( *javaEnv )->CallStaticObjectMethod( javaEnv , java_lang_class ,
                                                         method , javaClassName );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );

   /* pushes new object into lua stack */

   return pushJavaClass( L , classInstance );
#else
    return getClassMetatable (L, javaEnv, className, NULL);
#endif
}


/***************************************************************************
*
*  Function: createProxy
*  ****/
int createProxy( lua_State * L )
{
  jint ret;
  lua_Number stateIndex;
  const char * impl;
  jmethodID method;
  jthrowable exp;
  jstring str;
  JNIEnv * javaEnv;

  if ( lua_gettop( L ) != 2 )
  {
    lua_pushstring( L , "Error. Function createProxy expects 2 arguments." );
    lua_error( L );
  }

  /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   if ( !lua_isstring( L , 1 ) || !lua_istable( L , 2 ) )
   {
      lua_pushstring( L , "Invalid Argument types. Expected (string, table)." );
      lua_error( L );
   }

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "createProxyObject" ,
                                             "(ILjava/lang/String;)I" );

   impl = lua_tostring( L , 1 );

   str = ( *javaEnv )->NewStringUTF( javaEnv , impl );

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , str );
   
   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * cStr;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , str );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      cStr = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , cStr );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, cStr );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , str );

   return ret;
}

/***************************************************************************
*
*  Function: javaNew
*  ****/

int javaNew( lua_State * L )
{
   int top;
   jint ret;
   jclass clazz;
   jmethodID method;
   jobject classInstance ;
   jthrowable exp;
   jobject * userData;
   lua_Number stateIndex;
   JNIEnv * javaEnv;

   top = lua_gettop( L );

   if ( top == 0 )
   {
      lua_pushstring( L , "Error. Invalid number of parameters." );
      lua_error( L );
   }

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   /* Gets the java Class reference */
   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Argument not a valid Java Class." );
      lua_error( L );
   }

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   clazz = ( *javaEnv )->FindClass( javaEnv , "java/lang/Class" );

   userData = ( jobject * ) lua_touserdata( L , 1 );

   classInstance = ( jobject ) *userData;

   if ( ( *javaEnv )->IsInstanceOf( javaEnv , classInstance , clazz ) == JNI_FALSE )
   {
      lua_pushstring( L , "Argument not a valid Java Class." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "javaNew" , 
                                             "(ILjava/lang/Class;)I" );

   if ( clazz == NULL || method == NULL )
   {
      lua_pushstring( L , "Invalid method org.keplerproject.luajava.LuaJavaAPI.javaNew." );
      lua_error( L );
   }

   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , clazz , method , (jint)stateIndex , classInstance );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * str;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      str = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , str );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, str );

      lua_error( L );
   }
  return ret;
}


/***************************************************************************
*
*  Function: javaNewInstance
*  ****/

int javaNewInstance( lua_State * L )
{
   jint ret;
   jmethodID method;
   const char * className;
   jstring javaClassName;
   jthrowable exp;
   lua_Number stateIndex;
   JNIEnv * javaEnv;

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );

   /* get the string parameter */
   if ( !lua_isstring( L , 1 ) )
   {
      lua_pushstring( L , "Invalid parameter type. String expected as first parameter." );
      lua_error( L );
   }

   className = lua_tostring( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "javaNewInstance" ,
                                             "(ILjava/lang/String;)I" );

   javaClassName = ( *javaEnv )->NewStringUTF( javaEnv , className );
   
   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , 
                                            javaClassName );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * str;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      str = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , str );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, str );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );

   return ret;
}

static int javaNewArray (lua_State *L) {
   JNIEnv *javaEnv;
   lua_Number stateIndex;
   int type = 0;
   int ret;

   stateIndex = getJavaStateIndex (L);
    
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   type = lua_tonumber (L, lua_upvalueindex(1));

   ret = (*javaEnv)->CallStaticIntMethod (javaEnv, luajava_api_class,
                        luajava_newArray, (jint)stateIndex, (jint)type);
   
   //TODO check error
   return ret;
}


/***************************************************************************
*
*  Function: javaLoadLib
*  ****/

int javaLoadLib( lua_State * L )
{
   jint ret;
   int top;
   const char * className, * methodName;
   lua_Number stateIndex;
   jmethodID method;
   jthrowable exp;
   jstring javaClassName , javaMethodName;
   JNIEnv * javaEnv;

   top = lua_gettop( L );

   if ( top != 2 )
   {
      lua_pushstring( L , "Error. Invalid number of parameters." );
      lua_error( L );
   }

   /* Gets the luaState index */
   lua_pushstring( L , LUAJAVASTATEINDEX );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnumber( L , -1 ) )
   {
      lua_pushstring( L , "Impossible to identify luaState id." );
      lua_error( L );
   }

   stateIndex = lua_tonumber( L , -1 );
   lua_pop( L , 1 );


   if ( !lua_isstring( L , 1 ) || !lua_isstring( L , 2 ) )
   {
      lua_pushstring( L , "Invalid parameter. Strings expected." );
      lua_error( L );
   }

   className  = lua_tostring( L , 1 );
   methodName = lua_tostring( L , 2 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   method = ( *javaEnv )->GetStaticMethodID( javaEnv , luajava_api_class , "javaLoadLib" ,
                                             "(ILjava/lang/String;Ljava/lang/String;)I" );

   javaClassName  = ( *javaEnv )->NewStringUTF( javaEnv , className );
   javaMethodName = ( *javaEnv )->NewStringUTF( javaEnv , methodName );
   
   ret = ( *javaEnv )->CallStaticIntMethod( javaEnv , luajava_api_class , method, (jint)stateIndex , 
                                            javaClassName , javaMethodName );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * str;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );
      ( *javaEnv )->DeleteLocalRef( javaEnv , javaMethodName );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      str = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , str );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, str );

      lua_error( L );
   }

   ( *javaEnv )->DeleteLocalRef( javaEnv , javaClassName );
   ( *javaEnv )->DeleteLocalRef( javaEnv , javaMethodName );

   return ret;
}


/***************************************************************************
*
*  Function: pushJavaClass
*  ****/

int pushJavaClass( lua_State * L , jobject javaObject )
{

   jobject /** userData ,*/ globalRef;

   /* Gets the JNI Environment */
   JNIEnv * javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   return getClassMetatable(L, javaEnv, (jclass)javaObject, NULL);

#if 0
   globalRef = ( *javaEnv )->NewGlobalRef( javaEnv , javaObject );

   userData = ( jobject * ) lua_newuserdata( L , sizeof( jobject ) );
   *userData = globalRef;

   /* Creates metatable */
   lua_newtable( L );

   /* pushes the __index metamethod */
   lua_pushstring( L , LUAINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &classIndex );
   lua_rawset( L , -3 );

   /* pushes the __newindex metamethod */
   lua_pushstring( L , LUANEWINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &objectNewIndex );
   lua_rawset( L , -3 );

   /* pushes the __gc metamethod */
   lua_pushstring( L , LUAGCMETAMETHODTAG );
   lua_pushcfunction( L , &gc );
   lua_rawset( L , -3 );

   /* Is Java Object boolean */
   lua_pushstring( L , LUAJAVAOBJECTIND );
   lua_pushboolean( L , 1 );
   lua_rawset( L , -3 );

   if ( lua_setmetatable( L , -2 ) == 0 )
   {
		( *javaEnv )->DeleteGlobalRef( javaEnv , globalRef );
      lua_pushstring( L , "Cannot create proxy to java class." );
      lua_error( L );
   }

   return 1;
#endif
}


/***************************************************************************
*
*  Function: pushJavaObject
*  ****/

int pushJavaObject( lua_State * L , jobject javaObject )
{
   jobject * userData , globalRef;

   /* Gets the JNI Environment */
   JNIEnv * javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   if ((*javaEnv)->IsInstanceOf (javaEnv, javaObject, java_lang_class)) {
      return getClassMetatable(L, javaEnv, NULL, (jclass)javaObject);
   }

   globalRef = ( *javaEnv )->NewGlobalRef( javaEnv , javaObject );

   userData = ( jobject * ) lua_newuserdata( L , sizeof( jobject ) );
   *userData = globalRef;

#if 0
   /* Creates metatable */
   lua_newtable( L );

   /* pushes the __index metamethod */
   lua_pushstring( L , LUAINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &objectIndex );
   lua_rawset( L , -3 );

   /* pushes the __newindex metamethod */
   lua_pushstring( L , LUANEWINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &objectNewIndex );
   lua_rawset( L , -3 );

   /* pushes the __gc metamethod */
   lua_pushstring( L , LUAGCMETAMETHODTAG );
   lua_pushcfunction( L , &gc );
   lua_rawset( L , -3 );

   /* Is Java Object boolean */
   lua_pushstring( L , LUAJAVAOBJECTIND );
   lua_pushboolean( L , 1 );
   lua_rawset( L , -3 );
#else
   jclass clazz = (*javaEnv)->GetObjectClass (javaEnv, javaObject); 
   if (!getClassMetatable (L, javaEnv, NULL, clazz) && !lua_istable(L, -1)) {
       (*javaEnv)->DeleteGlobalRef( javaEnv, globalRef);
       (*javaEnv)->DeleteLocalRef( javaEnv, clazz);
       lua_pushstring (L, "cannot create proxy to java object ");
       lua_error (L);
       return 0;
   }
   (*javaEnv)->DeleteLocalRef (javaEnv, clazz);


#endif

   if ( lua_setmetatable( L , -2 ) == 0 )
   {
		( *javaEnv )->DeleteGlobalRef( javaEnv , globalRef );
      lua_pushstring( L , "Cannot create proxy to java object." );
      lua_error( L );
   }


   return 1;
}


/***************************************************************************
*
*  Function: pushJavaObject
*  ****/

int pushJavaArray( lua_State * L , jobject javaObject )
{
    return pushJavaObject(L, javaObject);
#if 0
   jobject * userData , globalRef;

   /* Gets the JNI Environment */
   JNIEnv * javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   globalRef = ( *javaEnv )->NewGlobalRef( javaEnv , javaObject );

   userData = ( jobject * ) lua_newuserdata( L , sizeof( jobject ) );
   *userData = globalRef;

   /* Creates metatable */
   lua_newtable( L );

   /* pushes the __index metamethod */
   lua_pushstring( L , LUAINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &arrayIndex );
   lua_rawset( L , -3 );

   /* pushes the __newindex metamethod */
   lua_pushstring( L , LUANEWINDEXMETAMETHODTAG );
   lua_pushcfunction( L , &arrayNewIndex );
   lua_rawset( L , -3 );

   /* pushes the __gc metamethod */
   lua_pushstring( L , LUAGCMETAMETHODTAG );
   lua_pushcfunction( L , &gc );
   lua_rawset( L , -3 );

   /* Is Java Object boolean */
   lua_pushstring( L , LUAJAVAOBJECTIND );
   lua_pushboolean( L , 1 );
   lua_rawset( L , -3 );

   if ( lua_setmetatable( L , -2 ) == 0 )
   {
		( *javaEnv )->DeleteGlobalRef( javaEnv , globalRef );
      lua_pushstring( L , "Cannot create proxy to java object." );
      lua_error( L );
   }

   return 1;
#endif
}


/***************************************************************************
*
*  Function: isJavaObject
*  ****/

int isJavaObject( lua_State * L , int idx )
{
   if ( lua_isuserdata( L , idx ) )
   {
      if ( lua_getmetatable( L , idx ) == 0 )
        return 0;
   }
   else if (lua_istable (L, idx ) )
   {
       lua_pushvalue (L, idx);
   }
   else
   {
       return 0;
   }


   lua_rawgeti( L , -1, 0); /* get the class_ref */

   if (!lua_isuserdata( L, -1 ))
   {
      lua_pop( L , 2 );
      return 0;
   }

   lua_pop( L , 2 );
   return 1;
}


/***************************************************************************
*
*  Function: getStateFromCPtr
*  ****/

lua_State * getStateFromCPtr( JNIEnv * env , jobject cptr )
{
   lua_State * L;

   jclass classPtr       = ( *env )->GetObjectClass( env , cptr );
   jfieldID CPtr_peer_ID = ( *env )->GetFieldID( env , classPtr , "peer" , "J" );
   jbyte * peer          = ( jbyte * ) ( *env )->GetLongField( env , cptr , CPtr_peer_ID );

   L = ( lua_State * ) peer;

   pushJNIEnv( env ,  L );

   return L;
}


/***************************************************************************
*
*  Function: luaJavaFunctionCall
*  ****/

int luaJavaFunctionCall( lua_State * L )
{
   jobject * obj;
   jthrowable exp;
   int ret;
   JNIEnv * javaEnv;
   
   if ( !isJavaObject( L , 1 ) )
   {
      lua_pushstring( L , "Not a java Function." );
      lua_error( L );
   }

   obj = getObjectOrClass( L , 1 );

   /* Gets the JNI Environment */
   javaEnv = getEnvFromState( L );
   if ( javaEnv == NULL )
   {
      lua_pushstring( L , "Invalid JNI Environment." );
      lua_error( L );
   }

   /* the Object must be an instance of the JavaFunction class */
   if ( ( *javaEnv )->IsInstanceOf( javaEnv , *obj , java_function_class ) ==
        JNI_FALSE )
   {
      fprintf( stderr , "Called Java object is not a JavaFunction\n");
      return 0;
   }

   ret = ( *javaEnv )->CallIntMethod( javaEnv , *obj , java_function_method );

   exp = ( *javaEnv )->ExceptionOccurred( javaEnv );

   /* Handles exception */
   if ( exp != NULL )
   {
      jobject jstr;
      const char * str;
      
      ( *javaEnv )->ExceptionClear( javaEnv );
      jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , get_message_method );

      if ( jstr == NULL )
      {
         jmethodID methodId;

         methodId = ( *javaEnv )->GetMethodID( javaEnv , throwable_class , "toString" , "()Ljava/lang/String;" );
         jstr = ( *javaEnv )->CallObjectMethod( javaEnv , exp , methodId );
      }

      str = ( *javaEnv )->GetStringUTFChars( javaEnv , jstr , NULL );

      lua_pushstring( L , str );

      ( *javaEnv )->ReleaseStringUTFChars( javaEnv , jstr, str );

      lua_error( L );
   }
   return ret;
}


/***************************************************************************
*
*  Function: luaJavaFunctionCall
*  ****/

JNIEnv * getEnvFromState( lua_State * L )
{
   JNIEnv ** udEnv;

   lua_pushstring( L , LUAJAVAJNIENVTAG );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isuserdata( L , -1 ) )
   {
      lua_pop( L , 1 );
      return NULL;
   }

   udEnv = ( JNIEnv ** ) lua_touserdata( L , -1 );

   lua_pop( L , 1 );

   return * udEnv;
}


/***************************************************************************
*
*  Function: pushJNIEnv
*  ****/

void pushJNIEnv( JNIEnv * env , lua_State * L )
{
   JNIEnv ** udEnv;

   lua_pushstring( L , LUAJAVAJNIENVTAG );
   lua_rawget( L , LUA_REGISTRYINDEX );

   if ( !lua_isnil( L , -1 ) )
   {
      udEnv = ( JNIEnv ** ) lua_touserdata( L , -1 );
      *udEnv = env;
      lua_pop( L , 1 );
   }
   else
   {
      lua_pop( L , 1 );
      udEnv = ( JNIEnv ** ) lua_newuserdata( L , sizeof( JNIEnv * ) );
      *udEnv = env;

      lua_pushstring( L , LUAJAVAJNIENVTAG );
      lua_insert( L , -2 );
      lua_rawset( L , LUA_REGISTRYINDEX );
   }
}

/*
** Assumes the table is on top of the stack.
*/
static void set_info (lua_State *L) {
	lua_pushliteral (L, "_COPYRIGHT");
	lua_pushliteral (L, "Copyright (C) 2003-2007 Kepler Project");
	lua_settable (L, -3);
	lua_pushliteral (L, "_DESCRIPTION");
	lua_pushliteral (L, "LuaJava is a script tool for Java");
	lua_settable (L, -3);
	lua_pushliteral (L, "_NAME");
	lua_pushliteral (L, "LuaJava");
	lua_settable (L, -3);
	lua_pushliteral (L, "_VERSION");
	lua_pushliteral (L, "1.1");
	lua_settable (L, -3);
}

////////////////////////////////////////////dongjj
static int initJavaMethods(JNIEnv* env)
{
    jclass clazz;
    luajava_getFieldValue_method
        = (*env)->GetStaticMethodID (env, luajava_api_class, "getFieldValue", "(ILjava/lang/reflect/Field;Ljava/lang/Object;)I");
    luajava_setFieldValue_method
        = (*env)->GetStaticMethodID (env, luajava_api_class, "setFieldValue", "(ILjava/lang/reflect/Field;Ljava/lang/Object;)I");
    luajava_callMethod_method
        = (*env)->GetStaticMethodID (env, luajava_api_class, "callMethod",
                "(ILorg/keplerproject/luajava/LuaJavaAPI$MethodCaller;Ljava/lang/Object;)I");
    luajava_getFieldOrMethod_method
        = (*env)->GetStaticMethodID (env, luajava_api_class, "getFieldOrMethod", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
    luajava_getWritableField_method
        = (*env)->GetStaticMethodID (env, luajava_api_class, "getWriteableField", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");

    luajava_createProxyObject_interface
        = (*env)->GetStaticMethodID (env, luajava_api_class, "createProxyObject", "(ILjava/lang/Class;I)I");

    luajava_arrayIndex
        = ( *env )->GetStaticMethodID( env , luajava_api_class , "arrayIndex" , "(ILjava/lang/Object;I)I" );

    luajava_arrayNewIndex
        = ( *env )->GetStaticMethodID( env , luajava_api_class , "arrayNewIndex" , "(ILjava/lang/Object;I)I" );

    luajava_newArray
        = (*env)->GetStaticMethodID (env, luajava_api_class, "newArray", "(II)I");
    luajava_javaNew
        = (*env)->GetStaticMethodID (env, luajava_api_class, "javaNew", "(ILjava/lang/Class;)I");

    java_lang_Class_getName = (*env)->GetMethodID(env, java_lang_class, "getName", "()Ljava/lang/String;");
    java_lang_Class_forName = (*env)->GetStaticMethodID(env, java_lang_class, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
    java_lang_Class_isArray = (*env)->GetMethodID (env, java_lang_class, "isArray", "()Z");

    clazz = (*env)->FindClass (env, "java/lang/Object");
    java_lang_Object_hashCode
        = (*env)->GetMethodID (env, clazz, "hashCode", "()I");
    java_lang_Object_toString
        = (*env)->GetMethodID (env, clazz, "toString", "()Ljava/lang/String;");

    (*env)->DeleteLocalRef (env, clazz);

    clazz = (*env)->FindClass (env, "java/lang/reflect/Field");
    java_lang_reflect_Field = (jclass) (*env)->NewGlobalRef (env, clazz);
    (*env)->DeleteLocalRef (env, clazz);
}



static int classGetField (lua_State *L) {
   jobject obj;
   jobject field;
   JNIEnv* env;
   int ret;

   env = (JNIEnv*) lua_touserdata (L, 1);
   obj = (jobject)lua_touserdata (L, 2);
   field = (jobject*) lua_touserdata (L, lua_upvalueindex(1));

   ret = (*env)->CallStaticIntMethod (env, luajava_api_class, luajava_getFieldValue_method, (jint)getJavaStateIndex(L), field, obj);
   PROCESS_EXCEPTION (env, L);
   return ret;
}

static int classSetField (lua_State *L) {
   jobject obj = NULL;
   jobject field;
   JNIEnv* env;
   jobject value;
   int is_class = 0;
   int ret;

   env = (JNIEnv*) lua_touserdata (L, 1);
   if (lua_istable(L, 2)) {
       is_class = 1;
   } else {
       obj = getObject(L, 2);
   }
   field = (jobject*) lua_touserdata (L, lua_upvalueindex(1));

   lua_remove (L, 2);
   lua_remove (L, 1);

   ret = (*env)->CallStaticIntMethod (env, luajava_api_class, luajava_setFieldValue_method, (jint)getJavaStateIndex(L), field, obj);
   PROCESS_EXCEPTION (env, L);
   return ret;
}

static  int classCallMethod (lua_State *L) {
   jobject method_call;
   JNIEnv* env;
   jobject obj = 0;
   int ret;

   env = getEnvFromState (L);
   method_call = (jobject*) lua_touserdata (L, lua_upvalueindex(1));

   if (lua_istable(L, 1)) {
      lua_rawgeti (L, 1, 0);
      obj = (jobject)lua_touserdata(L, -1);
      lua_pop(L, 1);
   } else if (lua_isuserdata(L, 1)) {
      obj = getObject(L, 1);
   }

   ret = (*env)->CallStaticIntMethod (env, luajava_api_class, luajava_callMethod_method, (jint)getJavaStateIndex(L), method_call, obj);
   PROCESS_EXCEPTION (env, L);
   return ret;
}

static int getFieldOrMethod( lua_State *L, JNIEnv * env, jobject obj, const char* key) {
   jstring str;
   jobject retobj;
   
   str = (*env)->NewStringUTF (env, key);
   retobj = (*env)->CallStaticObjectMethod (env, luajava_api_class, luajava_getFieldOrMethod_method, obj, str);

   if (retobj == 0) {
      lua_pushnil (L);
   } else if ( (*env)->IsInstanceOf( env, retobj, java_lang_reflect_Field)) {
      //push the classGetField
      lua_pushlightuserdata (L, (*env)->NewGlobalRef (env, retobj));
      lua_pushcclosure (L, classGetField, 1);
   } else {
      lua_pushlightuserdata (L, (*env)->NewGlobalRef (env, retobj));
      lua_pushcclosure (L, classCallMethod, 1);
   }

   (*env)->DeleteLocalRef (env, str);
   return 1;
}

static int getWriteableField ( lua_State *L, JNIEnv *env, jobject obj, const char* key) {
   jstring str;
   jobject retobj;
   
   str = (*env)->NewStringUTF (env, key);
   retobj = (*env)->CallStaticObjectMethod (env, luajava_api_class, luajava_getWritableField_method, obj, str);

   if ( (*env)->IsInstanceOf( env, retobj, java_lang_reflect_Field)) {
      //push the classGetField
      lua_pushlightuserdata (L, (*env)->NewGlobalRef (env, retobj));
      lua_pushcclosure (L, classSetField, 1);
   } else {
      lua_pushnil (L);
   }

   (*env)->DeleteLocalRef (env, str);
   return 1;
}

/********************************************************************
*
* Function : baseMetaObjectIndex
* 
*****/
static int classMetaObjectIndex (lua_State *L)
{
   const char* key;
   jstring str;
   JNIEnv *javaEnv;
   jobject obj;
   lua_Number stateIndex;
   int member_table;
   int is_class_mode = 0;

   stateIndex = getJavaStateIndex (L);
    
    
   CHECK(L, lua_isstring( L , -1 ) , "Invalid object index. Must be string." );

   javaEnv = getEnvFromState( L );
   CHECK (L, javaEnv != NULL,  "Invalid JNI Environment." );

   if (lua_istable(L, 1)) {
     /* is class mode */
     is_class_mode = 1;
   } else {
   }
   obj = getObjectOrClass( L, 1);

   if (!is_class_mode && javaObjectIsArray (javaEnv, obj)) {
       /* try get as an array */
       if (lua_isnumber(L, -1)) {
         int index =  lua_tointeger (L, - 1);
         int ret = (*javaEnv)->CallStaticIntMethod(javaEnv, luajava_api_class, luajava_arrayIndex,
                    (jint)stateIndex,  obj, (jint)index);
         PROCESS_EXCEPTION (javaEnv, L);
         return ret;
       } else if (lua_isstring (L, -1)) {
           key = lua_tostring( L , -1 );
           if (strcmp(key, "length") == 0) {
              jsize size = (*javaEnv)->GetArrayLength(javaEnv, (jarray)obj);
              PROCESS_EXCEPTION (javaEnv, L);
              lua_pushinteger (L, size);
              return 1;
           }
       }
       return 0;
   }


   key = lua_tostring( L , -1 );
   /* get class field/method table */
   lua_rawgeti (L, lua_upvalueindex(1), 1);
   member_table = lua_gettop (L);

   /* is exit */
   lua_pushstring (L, key);
   lua_rawget (L, member_table);

   if (lua_isnil (L, -1)) {
      /* try get the method */
      lua_pop (L, 1);
      getFieldOrMethod (L, javaEnv, obj, key);
      if (!lua_isnil (L, -1)) {
         /* insert to the field/method table */
         lua_pushstring (L, key);
         lua_pushvalue (L, -2);
         lua_rawset (L, member_table);
      } else if (is_class_mode) {
        char szClassName[512];
        const char* the_class_name = NULL;
        /* maybe is the embeded class */
        lua_rawgeti (L, lua_upvalueindex(1), 3); /*the class name */
        the_class_name = lua_tostring(L, -1);
        sprintf(szClassName, "%s$%s", the_class_name, key);
        lua_pop (L, 1);
        return getClassMetatable (L, javaEnv, szClassName, NULL);
      }
   }

   lua_remove (L, member_table);

   /* is null */
   if (lua_iscfunction (L, -1)) {
      lua_CFunction f = lua_tocfunction (L, -1);
      if (f == classGetField) {
         /* call the get field */
         lua_pushlightuserdata (L, javaEnv); //java object
         lua_pushlightuserdata (L, obj);
         lua_pcall (L, 2, 1, 0);
      }
   }
   return 1;
}


static int classMetaObjectNewIndex ( lua_State * L )
{
   const char* key;
   jstring str;
   JNIEnv *javaEnv;
   jobject obj;
   lua_Number stateIndex;
   int member_table;
   int is_class_mode = 0;

   stateIndex = getJavaStateIndex (L);
    
    
   CHECK(L, lua_isstring( L , -1 ) , "Invalid object index. Must be string." );

   key = lua_tostring( L , -1 );

   CHECK(L, javaEnv = getEnvFromState( L ),  "Invalid JNI Environment." );
    
   if (lua_istable(L, 1)) {
      is_class_mode = 1;
   }
   obj = getObjectOrClass (L, 1);

   /* try set as array */
   if (!is_class_mode && lua_isnumber (L, 2)) {
       int index = lua_tointeger(L, 2);
       int ret = (*javaEnv)->CallStaticIntMethod(javaEnv, luajava_api_class, luajava_arrayNewIndex,
                    (jint)stateIndex, obj, (jint)index);
       PROCESS_EXCEPTION (javaEnv, L);
       return 0;
   }

   /* get class field set table */
   lua_rawgeti (L, lua_upvalueindex(1), 2);
   member_table = lua_gettop (L);

   /* is exit */
   lua_pushstring (L, key);
   lua_rawget (L, member_table);

   if (lua_isnil (L, -1)) {
      /* try get the method */
      lua_pop (L, 1);
      getWriteableField (L, javaEnv, obj, key);
      if (!lua_isnil (L, -1)) {
         /* insert to the field/method table */
         lua_pushvalue (L, -1);
         lua_pushstring (L, key);
         lua_rawset (L, member_table);
      }
   }

   if (lua_iscfunction (L, -1)) {
      /* call the set field */
      lua_pushlightuserdata (L, javaEnv); //java object
      lua_pushvalue (L, 3); //value
      lua_pushvalue (L, 1); //user data
      lua_pcall (L, 3, 1, 0);
   }

   lua_pop (L, lua_gettop(L));

   return 0;
}

static int classMetaGC (lua_State *L)
{
   //TODO
   return 0;
}

static int classMetaToString (lua_State *L)
{
  jobject obj;
  JNIEnv *env;
  jstring retstr;
  const char* str;

  env = getEnvFromState(L);
  obj = getObjectOrClass (L, 1);

  retstr = (jstring)(*env)->CallObjectMethod (env, obj, java_lang_Object_toString);

  if (retstr != NULL)
  {
     str = (*env)->GetStringUTFChars (env, retstr, NULL);
  
     lua_pushstring (L, str);

     (*env)->ReleaseStringUTFChars(env, retstr, str);
  } else {
     char sz[100];
     sprintf(sz, "object %p\n", obj);
     lua_pushstring (L, sz);
  }

  lua_remove (L, 1);
  
  return 1;
}

static const char* getClassName(JNIEnv *env, jclass clazz, char* buff) {
    jstring jstr = (*env)->CallObjectMethod(env, (jobject)clazz, java_lang_Class_getName);
    const char* str;
    if (jstr == NULL) {
        return NULL;
    }
    
    str = (*env)->GetStringUTFChars(env, jstr, NULL);
    strcpy(buff, str);
    (*env)->ReleaseStringUTFChars(env, jstr, str);
    (*env)->DeleteLocalRef(env, jstr);
    return buff;
}

/*************************************************************************
*
* Function : createBaseMetatable
* ******/
void createBaseMetatable( lua_State * L)
{
    lua_newtable (L); 
    int table = lua_gettop (L);

    /* save to registy index */
    lua_pushvalue (L, -1);
    lua_setfield (L, LUA_REGISTRYINDEX, LUABASEMETATABLE);

    /* create a metatable for global table*/
    /* is used to for java global referance */
    
    lua_pop(L, 1);
}

static int classCreateProxy(lua_State *L) {
    lua_Number stateIndex;
    JNIEnv *   env;
    jclass     intf;


    if (lua_gettop(L) != 2) {
        luaL_error(L, "Error. Function createProxy expects 2 arguments.");
    }

    stateIndex = getJavaStateIndex (L);
    
    env = getEnvFromState(L);
    
    intf = (jclass) getObjectOrClass (L, 1);
    if (intf == NULL) {
        luaL_error (L, "Error. createProxy must be a interface.");
    }

    return (*env)->CallStaticIntMethod (env, luajava_api_class, luajava_createProxyObject_interface, (jint)stateIndex, intf, 2);    
}

static int classNewInstance (lua_State *L) {
    lua_Number stateIndex;
    JNIEnv *   env;
    jclass     clazz;
    int        ret = 0;
 
    stateIndex = getJavaStateIndex (L);
    
    env = getEnvFromState(L);
    
    clazz = getClassFromUpvalue (L);

    ret = (*env)->CallStaticIntMethod (env, luajava_api_class, luajava_javaNew, (jint)stateIndex, clazz);
    
    PROCESS_EXCEPTION (env, L);
    return ret;
}

static void createClassForGlobal (lua_State *L, int class_mt, const char* class_name) {
    char szname[1024];
    int i = 0;
    int last = 0;
    int is_anymous = 1;
    const char* strname;
    while (class_name[i])
    {
        if (class_name[i] == '.' || class_name[i] == '$') {
            if (is_anymous) {
                return ;//ignore anymous
            }
            szname[i] = 0;
            last = i + 1;
        } else {
            szname[i] = class_name[i];
            if (is_anymous && !(szname[i] >= '0' && szname[i] <= '9')) {
                is_anymous = 0;
            }
        }
        i ++;
    }
    if (is_anymous) {
        return ; //ignore anymous
    }
    szname[i+1] = 0;
    
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    i = 0;
    while (i < last) {
        strname = szname + i;
        lua_pushstring (L,strname);
        lua_rawget(L, -2);
        if (lua_isnil(L, -1)) {
            //create table
            lua_pop (L, 1);
            lua_newtable (L);
            lua_pushstring(L,strname);
            lua_pushvalue (L, -2);
            lua_rawset (L, -4);
        }
        lua_remove (L, -2);
        i += strlen(strname);
    }
    
    strname = szname + last;
    lua_pushstring (L, strname);
    lua_pushvalue (L, class_mt);
    lua_rawset (L, -3);
    lua_pop (L, 1);
}


/************************************************************************
*
* Function : getClassMetatable
* ******/
int getClassMetatable( lua_State *L, JNIEnv *env,  const char* class_name, jclass clazz)
{
    char szClassName[256];
    int  delete_class = 0;
    int  base_mt;
    int  class_mt;
    int  class_hashcode;
    
    if (class_name == NULL && clazz == NULL) {
        return 0;
    }

    /* get the base metatable */
    lua_getfield (L, LUA_REGISTRYINDEX, LUABASEMETATABLE);
    base_mt = lua_gettop (L);

    if (clazz != NULL)
    {
       class_hashcode = getObjectHashCode(env, (jobject)clazz);
       
       lua_pushinteger (L, class_hashcode);
       lua_rawget (L, base_mt);

       if (lua_istable (L, -1)) {
          lua_remove (L, base_mt);
          return 1;
       }
       lua_pop (L, 1);
    }

    if (class_name == NULL) {
        class_name = getClassName(env, clazz, szClassName);
    }

    lua_pushstring (L, class_name);
    lua_rawget (L, base_mt);

    if (lua_istable (L, -1)) {
        lua_remove (L, base_mt);
        return 1;
    }
    lua_pop(L, 1);

    /* create the new metatable */

    if (clazz == NULL) {
        clazz = classForName(env, class_name);
        PROCESS_EXCEPTION (env, L);
        delete_class = 1;
        class_hashcode = getObjectHashCode(env, (jobject)clazz);
    }

    /* save class to class_mt[0] */
    jclass class_ref = (jclass)(*env)->NewGlobalRef (env, clazz);
 
    /* new class table */
    lua_newtable (L);
    class_mt = lua_gettop (L);

    /* save to base_mt */
    lua_pushvalue (L, -1);
    lua_pushstring (L, class_name);
    lua_rawset (L, base_mt);

    lua_pushvalue (L, class_mt);
    lua_pushinteger (L, class_hashcode);
    lua_rawset (L, base_mt);

    /* push class index to 0 */
    lua_pushlightuserdata (L, class_ref);
    lua_rawseti (L, class_mt, 0);

    /* push the field/method table at 1*/
    lua_newtable (L);
    lua_rawseti (L, class_mt, 1);

    /* push the set field table at 2 */
    lua_newtable (L);
    lua_rawseti (L, class_mt, 2);

    /* push the class name at 3 */
    lua_pushstring (L, class_name);
    lua_rawseti (L, class_mt, 3);

    /* push the __index metamethod */
    lua_pushstring (L, LUAINDEXMETAMETHODTAG);
    lua_pushvalue (L, class_mt);
    lua_pushcclosure (L, &classMetaObjectIndex, 1);
    lua_rawset (L, class_mt);

    /* push the __newindex metamethod */
    lua_pushstring (L, LUANEWINDEXMETAMETHODTAG);
    lua_pushvalue (L, class_mt);
    lua_pushcclosure (L, &classMetaObjectNewIndex, 1);
    lua_rawset (L, class_mt);

    /* push the __gc metamethod */
    lua_pushstring (L, LUAGCMETAMETHODTAG);
    lua_pushvalue (L, class_mt);
    lua_pushcclosure (L, &classMetaGC, 1);
    lua_rawset (L, class_mt);

    /* push the __tostring metamethod */
    lua_pushstring (L, LUATOSTRINGMETAMETHODTAG);
    lua_pushvalue (L, class_mt);
    lua_pushcclosure (L, &classMetaToString, 1);
    lua_rawset (L, class_mt);

    /* push the createProxy method */
    lua_pushstring (L, "createProxy");
    lua_pushcfunction(L, &classCreateProxy);
    lua_rawset (L, class_mt);

    /* push the __call for class */
    lua_pushstring (L, "__call");
    lua_pushvalue (L, class_mt);
    lua_pushcclosure (L, &classNewInstance, 1);
    lua_rawset (L, class_mt);

    /* set the metatable as itself */
    lua_pushvalue (L, class_mt);
    lua_setmetatable (L, class_mt);

    /* create the table for global */
    createClassForGlobal(L, class_mt, class_name);

    /* remove the base_mt */
    lua_remove (L, base_mt);

    if (delete_class) {
        (*env) ->DeleteLocalRef (env, clazz);
    }

    return 1;
}


/**************************** JNI FUNCTIONS ****************************/

/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

static void installNewArray(lua_State *L, const char* name, char type) {
    lua_pushstring (L, name);
    lua_pushinteger (L, type);
    lua_pushcclosure (L, &javaNewArray, 1);
    lua_settable (L, -3);
}


JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState_luajava_1open
  ( JNIEnv * env , jobject jobj , jobject cptr , jint stateId )
{
  lua_State* L;

  jclass tempClass;

  L = getStateFromCPtr( env , cptr );

  lua_pushstring( L , LUAJAVASTATEINDEX );
  lua_pushnumber( L , (lua_Number)stateId );
  lua_settable( L , LUA_REGISTRYINDEX );


  lua_newtable( L );

  lua_setglobal( L , "luajava" );

  lua_getglobal( L , "luajava" );
  
  set_info( L);
  
  lua_pushstring( L , "bindClass" );
  lua_pushcfunction( L , &javaBindClass );
  lua_settable( L , -3 );

  lua_pushstring( L , "new" );
  lua_pushcfunction( L , &javaNew );
  lua_settable( L , -3 );

  lua_pushstring( L , "newInstance" );
  lua_pushcfunction( L , &javaNewInstance );
  lua_settable( L , -3 );

  lua_pushstring( L , "loadLib" );
  lua_pushcfunction( L , &javaLoadLib );
  lua_settable( L , -3 );

  lua_pushstring( L , "createProxy" );
  lua_pushcfunction( L , &createProxy );
  lua_settable( L , -3 );

  installNewArray(L , "newArray",'L');
  installNewArray(L , "newIntArray",'I');
  installNewArray(L , "newLongArray",'J');
  installNewArray(L , "newDoubleArray",'D');
  installNewArray(L , "newFloatArray",'F');
  installNewArray(L , "newBooleanArray",'Z');
  installNewArray(L , "newCharArray",'C');
  installNewArray(L , "newByteArray",'B');
  installNewArray(L , "newShortArray",'S');


  lua_pop( L , 1 );

  if ( luajava_api_class == NULL )
  {
    CHECK(L, tempClass = ( *env )->FindClass( env , "org/keplerproject/luajava/LuaJavaAPI" )
                , "Could not find LuaJavaAPI class");

    CHECK(L, luajava_api_class = ( *env )->NewGlobalRef( env , tempClass )
                , "Could not bind to LuaJavaAPI class" );
  }

  if ( java_function_class == NULL )
  {
    CHECK(L, tempClass = ( *env )->FindClass( env , "org/keplerproject/luajava/JavaFunction" )
            , "Could not find JavaFunction interface\n" );

    CHECK(L, java_function_class = ( *env )->NewGlobalRef( env , tempClass )
            , "Could not bind to JavaFunction interface\n" );
  }

  if ( java_function_method == NULL )
  {
    CHECK(L, java_function_method = ( *env )->GetMethodID( env , java_function_class , "execute" , "()I")
            , "Could not find <execute> method in JavaFunction\n" );
  }

  if ( throwable_class == NULL )
  {
    CHECK(L, tempClass = ( *env )->FindClass( env , "java/lang/Throwable" )
                , "Error. Couldn't bind java class java.lang.Throwable\n" );

    CHECK(L, throwable_class = ( *env )->NewGlobalRef( env , tempClass )
                , "Error. Couldn't bind java class java.lang.Throwable\n" );
  }

  if ( get_message_method == NULL )
  {
    CHECK(L, get_message_method = ( *env )->GetMethodID( env , throwable_class , "getMessage" ,
                                                "()Ljava/lang/String;" )
                , "Could not find <getMessage> method in java.lang.Throwable\n");
  }

  if ( java_lang_class == NULL )
  {
    CHECK(L, tempClass = ( *env )->FindClass( env , "java/lang/Class" )
                , "Error. Coundn't bind java class java.lang.Class\n" );

    CHECK(L, java_lang_class = ( *env )->NewGlobalRef( env , tempClass )
                , "Error. Couldn't bind java class java.lang.Throwable\n" );
  }

  initJavaMethods (env);

  createBaseMetatable (L);

  pushJNIEnv( env , L );
}

/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT jobject JNICALL Java_org_keplerproject_luajava_LuaState__1getObjectFromUserdata
  (JNIEnv * env , jobject jobj , jobject cptr , jint index )
{
   /* Get luastate */
   lua_State * L = getStateFromCPtr( env , cptr );
   jobject *   obj;

   if ( !isJavaObject( L , index ) )
   {
      ( *env )->ThrowNew( env , ( *env )->FindClass( env , "java/lang/Exception" ) ,
                          "Index is not a java object" );
      return NULL;
   }

   return getObjectOrClass (L, index);
}


/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT jboolean JNICALL Java_org_keplerproject_luajava_LuaState__1isObject
  (JNIEnv * env , jobject jobj , jobject cptr , jint index )
{
   /* Get luastate */
   lua_State * L = getStateFromCPtr( env , cptr );

   return (isJavaObject( L , index ) ? JNI_TRUE : JNI_FALSE );
}


/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushJavaObject
  (JNIEnv * env , jobject jobj , jobject cptr , jobject obj )
{
   /* Get luastate */
   lua_State* L = getStateFromCPtr( env , cptr );

   pushJavaObject( L , obj );
}


/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushJavaArray
  (JNIEnv * env , jobject jobj , jobject cptr , jobject obj )
{
   /* Get luastate */
   lua_State* L = getStateFromCPtr( env , cptr );

	pushJavaArray( L , obj );
}


/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushJavaFunction
  (JNIEnv * env , jobject jobj , jobject cptr , jobject obj )
{
   /* Get luastate */
   lua_State* L = getStateFromCPtr( env , cptr );

   jobject * userData , globalRef;

   globalRef = ( *env )->NewGlobalRef( env , obj );

   userData = ( jobject * ) lua_newuserdata( L , sizeof( jobject ) );
   *userData = globalRef;

   /* Creates metatable */
   lua_newtable( L );

   /* pushes the __index metamethod */
   lua_pushstring( L , LUACALLMETAMETHODTAG );
   lua_pushcfunction( L , &luaJavaFunctionCall );
   lua_rawset( L , -3 );

   /* pusher the __gc metamethod */
   lua_pushstring( L , LUAGCMETAMETHODTAG );
   lua_pushcfunction( L , &gc );
   lua_rawset( L , -3 );

   lua_pushstring( L , LUAJAVAOBJECTIND );
   lua_pushboolean( L , 1 );
   lua_rawset( L , -3 );

   if ( lua_setmetatable( L , -2 ) == 0 )
   {
      ( *env )->ThrowNew( env , ( *env )->FindClass( env , "org/keplerproject/luajava/LuaException" ) ,
                          "Index is not a java object" );
   }
}


/************************************************************************
*   JNI Called function
*      LuaJava API Function
************************************************************************/

JNIEXPORT jboolean JNICALL Java_org_keplerproject_luajava_LuaState__1isJavaFunction
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   /* Get luastate */
   lua_State* L = getStateFromCPtr( env , cptr );
   jobject * obj;

   if ( !isJavaObject( L , idx ) )
   {
      return JNI_FALSE;
   }

   obj = getObjectOrClass( L , idx );

   return ( *env )->IsInstanceOf( env , *obj , java_function_class );

}


/*********************** LUA API FUNCTIONS ******************************/

/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jobject JNICALL Java_org_keplerproject_luajava_LuaState__1open
  (JNIEnv * env , jobject jobj)
{
   lua_State * L = lua_open();

   jobject obj;
   jclass tempClass;

   tempClass = ( *env )->FindClass( env , "org/keplerproject/luajava/CPtr" );
    
   obj = ( *env )->AllocObject( env , tempClass );
   if ( obj )
   {
      ( *env )->SetLongField( env , obj , ( *env )->GetFieldID( env , tempClass , "peer", "J" ) , ( jlong ) L );
   }
   return obj;

}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openBase
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_base( L );
   lua_pushcfunction( L , luaopen_base );
   lua_pushstring( L , "" );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openTable
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_table( L );
   lua_pushcfunction( L , luaopen_table );
   lua_pushstring( L , LUA_TABLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openIo
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_io( L );
   lua_pushcfunction( L , luaopen_io );
   lua_pushstring( L , LUA_IOLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openOs
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_os( L );
   lua_pushcfunction( L , luaopen_os );
   lua_pushstring( L , LUA_OSLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openString
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_string( L );
   lua_pushcfunction( L , luaopen_string );
   lua_pushstring( L , LUA_STRLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openMath
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_math( L );
   lua_pushcfunction( L , luaopen_math );
   lua_pushstring( L , LUA_MATHLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openDebug
  (JNIEnv * env, jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_debug( L );
   lua_pushcfunction( L , luaopen_debug );
   lua_pushstring( L , LUA_DBLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openPackage
  (JNIEnv * env, jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   //luaopen_package( L );
   lua_pushcfunction( L , luaopen_package );
   lua_pushstring( L , LUA_LOADLIBNAME );
   lua_call(L , 1 , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1openLibs
  (JNIEnv * env, jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_openlibs( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1close
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_close( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jobject JNICALL Java_org_keplerproject_luajava_LuaState__1newthread
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );
   lua_State * newThread;
   
   jobject obj;
   jclass tempClass;
    
   newThread = lua_newthread( L );

   tempClass = ( *env )->FindClass( env , "org/keplerproject/luajava/CPtr" );
   obj = ( *env )->AllocObject( env , tempClass );
   if ( obj )
   {
      ( *env )->SetLongField( env , obj , ( *env )->GetFieldID( env , tempClass ,
                                                        "peer" , "J" ), ( jlong ) L );
   }

   return obj;

}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1getTop
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_gettop( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1setTop
  (JNIEnv * env , jobject jobj , jobject cptr , jint top)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_settop( L , ( int ) top );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushValue
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pushvalue( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1remove
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_remove( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1insert
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_insert( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1replace
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_replace( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1checkStack
  (JNIEnv * env , jobject jobj , jobject cptr , jint sz)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_checkstack( L , ( int ) sz );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1xmove
  (JNIEnv * env , jobject jobj , jobject from , jobject to , jint n)
{
   lua_State * fr = getStateFromCPtr( env , from );
   lua_State * t  = getStateFromCPtr( env , to );

   lua_xmove( fr , t , ( int ) n );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isNumber
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isnumber( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isString
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isstring( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isFunction
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isfunction( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isCFunction
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_iscfunction( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isUserdata
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isuserdata( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_istable( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isBoolean
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isboolean( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isNil
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isnil( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isNone
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isnone( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1isNoneOrNil
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_isnoneornil( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1type
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_type( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1typeName
  (JNIEnv * env , jobject jobj , jobject cptr , jint tp)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * name = lua_typename( L , tp );

   return ( *env )->NewStringUTF( env , name );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1equal
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx1 , jint idx2)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_equal( L , idx1 , idx2 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1rawequal
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx1 , jint idx2)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_rawequal( L , idx1 , idx2 );
}

  
/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1lessthan
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx1 , jint idx2)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_lessthan( L , idx1 ,idx2 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jdouble JNICALL Java_org_keplerproject_luajava_LuaState__1toNumber
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jdouble ) lua_tonumber( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1toInteger
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_tointeger( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1toBoolean
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_toboolean( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1toString
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * str = lua_tostring( L , idx );

   return ( *env )->NewStringUTF( env , str );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1strlen
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_strlen( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1objlen
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_objlen( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jobject JNICALL Java_org_keplerproject_luajava_LuaState__1toThread
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L , * thr;

   jobject obj;
   jclass tempClass;

   L = getStateFromCPtr( env , cptr );

   thr = lua_tothread( L , ( int ) idx );

   tempClass = ( *env )->FindClass( env , "org/keplerproject/luajava/CPtr" );
    
   obj = ( *env )->AllocObject( env , tempClass );
   if ( obj )
   {
      ( *env )->SetLongField( env , obj , ( *env )->GetFieldID( env , tempClass , "peer", "J" ) , ( jlong ) thr );
   }
   return obj;

}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushNil
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pushnil( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushNumber
  (JNIEnv * env , jobject jobj , jobject cptr , jdouble number)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pushnumber( L , ( lua_Number ) number );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushInteger
  (JNIEnv * env , jobject jobj , jobject cptr , jint number)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pushinteger( L, ( lua_Integer ) number );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushString__Lorg_keplerproject_luajava_CPtr_2Ljava_lang_String_2
  (JNIEnv * env , jobject jobj , jobject cptr , jstring str)
{
   lua_State * L = getStateFromCPtr( env , cptr );
   const char * uniStr;

   uniStr =  ( *env )->GetStringUTFChars( env , str , NULL );

   if ( uniStr == NULL )
      return;

   lua_pushstring( L , uniStr );
   
   ( *env )->ReleaseStringUTFChars( env , str , uniStr );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushString__Lorg_keplerproject_luajava_CPtr_2_3BI
  (JNIEnv * env , jobject jobj , jobject cptr , jbyteArray bytes , jint n)
{
   lua_State * L = getStateFromCPtr( env , cptr );
   char * cBytes;
   
   cBytes = ( char * ) ( *env )->GetByteArrayElements( env , bytes, NULL );
   
   lua_pushlstring( L , cBytes , n );
   
   ( *env )->ReleaseByteArrayElements( env , bytes , cBytes , 0 );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushBoolean
  (JNIEnv * env , jobject jobj , jobject cptr , jint jbool)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pushboolean( L , ( int ) jbool );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1getTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_gettable( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1getField
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx , jstring k)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * uniStr;
   uniStr =  ( *env )->GetStringUTFChars( env , k , NULL );

   lua_getfield( L , ( int ) idx , uniStr );
   
   ( *env )->ReleaseStringUTFChars( env , k , uniStr );
}

/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1rawGet
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_rawget( L , (int)idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1rawGetI
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx, jint n)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_rawgeti( L , idx , n );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1createTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint narr , jint nrec)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_createtable( L , ( int ) narr , ( int ) nrec );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1newTable
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_newtable( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1getMetaTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return lua_getmetatable( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1getFEnv
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_getfenv( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1setTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_settable( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1setField
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx , jstring k)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * uniStr;
   uniStr =  ( *env )->GetStringUTFChars( env , k , NULL );

   lua_setfield( L , ( int ) idx , uniStr );
   
   ( *env )->ReleaseStringUTFChars( env , k , uniStr );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1rawSet
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_rawset( L , (int)idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1rawSetI
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx, jint n)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_rawseti( L , idx , n );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1setMetaTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return lua_setmetatable( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1setFEnv
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return lua_setfenv( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1call
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArgs , jint nResults)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_call( L , nArgs , nResults );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1pcall
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArgs , jint nResults , jint errFunc)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_pcall( L , nArgs , nResults , errFunc );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1yield
  (JNIEnv * env , jobject jobj , jobject cptr , jint nResults)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_yield( L , nResults );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1resume
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArgs)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_resume( L , nArgs );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1status
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_status( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1gc
  (JNIEnv * env , jobject jobj , jobject cptr , jint what , jint data)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_gc( L , what , data );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1getGcCount
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_getgccount( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1next
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_next( L , idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1error
  (JNIEnv * env , jobject jobj , jobject cptr)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) lua_error( L );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1concat
  (JNIEnv * env , jobject jobj , jobject cptr , jint n)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_concat( L , n );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pop
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   lua_pop( L , ( int ) idx );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1setGlobal
  (JNIEnv * env , jobject jobj , jobject cptr , jstring name)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * str = ( *env )->GetStringUTFChars( env , name, NULL );

   lua_setglobal( L , str );

   ( *env )->ReleaseStringUTFChars( env , name , str );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1getGlobal
  (JNIEnv * env , jobject jobj , jobject cptr , jstring name)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * str = ( *env )->GetStringUTFChars( env , name, NULL );

   lua_getglobal( L , str );

   ( *env )->ReleaseStringUTFChars( env , name , str );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LdoFile
  (JNIEnv * env , jobject jobj , jobject cptr , jstring fileName)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * file = ( *env )->GetStringUTFChars( env , fileName, NULL );

   int ret;

   ret = luaL_dofile( L , file );

   ( *env )->ReleaseStringUTFChars( env , fileName , file );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LdoString
  (JNIEnv * env , jobject jobj , jobject cptr , jstring str)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   const char * utfStr = ( * env )->GetStringUTFChars( env , str , NULL );

   int ret;

   ret = luaL_dostring( L , utfStr );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LgetMetaField
  (JNIEnv * env , jobject jobj , jobject cptr , jint obj , jstring e)
{
   lua_State * L    = getStateFromCPtr( env , cptr );
   const char * str = ( *env )->GetStringUTFChars( env , e , NULL );
   int ret;

   ret = luaL_getmetafield( L , ( int ) obj , str );

   ( *env )->ReleaseStringUTFChars( env , e , str );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LcallMeta
  (JNIEnv * env , jobject jobj , jobject cptr , jint obj , jstring e)
{
   lua_State * L    = getStateFromCPtr( env , cptr );
   const char * str = ( *env )->GetStringUTFChars( env , e , NULL );
   int ret;

   ret = luaL_callmeta( L , ( int ) obj, str );

   ( *env )->ReleaseStringUTFChars( env , e , str );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1Ltyperror
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArg , jstring tName)
{
   lua_State * L     = getStateFromCPtr( env , cptr );
   const char * name = ( *env )->GetStringUTFChars( env , tName , NULL );
   int ret;

   ret = luaL_typerror( L , ( int ) nArg , name );

   ( *env )->ReleaseStringUTFChars( env , tName , name );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LargError
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg , jstring extraMsg)
{
   lua_State * L    = getStateFromCPtr( env , cptr );
   const char * msg = ( *env )->GetStringUTFChars( env , extraMsg , NULL );
   int ret;

   ret = luaL_argerror( L , ( int ) numArg , msg );

   ( *env )->ReleaseStringUTFChars( env , extraMsg , msg );

   return ( jint ) ret;;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckString
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg)
{
   lua_State * L = getStateFromCPtr( env , cptr );
   const char * res;

   res = luaL_checkstring( L , ( int ) numArg );

   return ( *env )->NewStringUTF( env , res );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1LoptString
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg , jstring def)
{
   lua_State * L  = getStateFromCPtr( env , cptr );
   const char * d = ( *env )->GetStringUTFChars( env , def , NULL );
   const char * res;
   jstring ret;

   res = luaL_optstring( L , ( int ) numArg , d );

   ret = ( *env )->NewStringUTF( env , res );

   ( *env )->ReleaseStringUTFChars( env , def , d );

   return ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jdouble JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckNumber
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jdouble ) luaL_checknumber( L , ( int ) numArg );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jdouble JNICALL Java_org_keplerproject_luajava_LuaState__1LoptNumber
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg , jdouble def)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jdouble ) luaL_optnumber( L , ( int ) numArg , ( lua_Number ) def );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckInteger
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) luaL_checkinteger( L , ( int ) numArg );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LoptInteger
  (JNIEnv * env , jobject jobj , jobject cptr , jint numArg , jint def)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) luaL_optinteger( L , ( int ) numArg , ( lua_Integer ) def );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckStack
  (JNIEnv * env , jobject jobj , jobject cptr , jint sz , jstring msg)
{
   lua_State * L  = getStateFromCPtr( env , cptr );
   const char * m = ( *env )->GetStringUTFChars( env , msg , NULL );

   luaL_checkstack( L , ( int ) sz , m );

   ( *env )->ReleaseStringUTFChars( env , msg , m );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckType
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArg , jint t)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_checktype( L , ( int ) nArg , ( int ) t );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LcheckAny
  (JNIEnv * env , jobject jobj , jobject cptr , jint nArg)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_checkany( L , ( int ) nArg );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LnewMetatable
  (JNIEnv * env , jobject jobj , jobject cptr , jstring tName)
{
   lua_State * L     = getStateFromCPtr( env , cptr );
   const char * name = ( *env )->GetStringUTFChars( env , tName , NULL );
   int ret;

   ret = luaL_newmetatable( L , name );

   ( *env )->ReleaseStringUTFChars( env , tName , name );

   return ( jint ) ret;;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LgetMetatable
  (JNIEnv * env , jobject jobj , jobject cptr , jstring tName)
{
   lua_State * L     = getStateFromCPtr( env , cptr );
   const char * name = ( *env )->GetStringUTFChars( env , tName , NULL );

   luaL_getmetatable( L , name );

   ( *env )->ReleaseStringUTFChars( env , tName , name );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1Lwhere
  (JNIEnv * env , jobject jobj , jobject cptr , jint lvl)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_where( L , ( int ) lvl );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1Lref
  (JNIEnv * env , jobject jobj , jobject cptr , jint t)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) luaL_ref( L , ( int ) t );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LunRef
  (JNIEnv * env , jobject jobj , jobject cptr , jint t , jint ref)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_unref( L , ( int ) t , ( int ) ref );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LgetN
  (JNIEnv * env , jobject jobj , jobject cptr , jint t)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   return ( jint ) luaL_getn( L , ( int ) t );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1LsetN
  (JNIEnv * env , jobject jobj , jobject cptr , jint t , jint n)
{
   lua_State * L = getStateFromCPtr( env , cptr );

   luaL_setn( L , ( int ) t , ( int ) n );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LloadFile
  (JNIEnv * env , jobject jobj , jobject cptr , jstring fileName)
{
   lua_State * L   = getStateFromCPtr( env , cptr );
   const char * fn = ( *env )->GetStringUTFChars( env , fileName , NULL );
   int ret;

   ret = luaL_loadfile( L , fn );

   ( *env )->ReleaseStringUTFChars( env , fileName , fn );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LloadBuffer
  (JNIEnv * env , jobject jobj , jobject cptr , jbyteArray buff , jlong sz , jstring n)
{
   lua_State * L = getStateFromCPtr( env , cptr );
   jbyte * cBuff = ( *env )->GetByteArrayElements( env , buff, NULL );
   const char * name = ( * env )->GetStringUTFChars( env , n , NULL );
   int ret;

   ret = luaL_loadbuffer( L , ( const char * ) cBuff, ( int ) sz, name );

   ( *env )->ReleaseStringUTFChars( env , n , name );

   ( *env )->ReleaseByteArrayElements( env , buff , cBuff , 0 );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jint JNICALL Java_org_keplerproject_luajava_LuaState__1LloadString
  (JNIEnv * env , jobject jobj , jobject cptr , jstring str)
{
   lua_State * L   = getStateFromCPtr( env , cptr );
   const char * fn = ( *env )->GetStringUTFChars( env , str , NULL );
   int ret;

   ret = luaL_loadstring( L , fn );

   ( *env )->ReleaseStringUTFChars( env , str , fn );

   return ( jint ) ret;
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1Lgsub
  (JNIEnv * env , jobject jobj , jobject cptr , jstring s , jstring p , jstring r)
{
   lua_State * L   = getStateFromCPtr( env , cptr );
   const char * utS = ( *env )->GetStringUTFChars( env , s , NULL );
   const char * utP = ( *env )->GetStringUTFChars( env , p , NULL );
   const char * utR = ( *env )->GetStringUTFChars( env , r , NULL );

   const char * sub = luaL_gsub( L , utS , utP , utR );

   ( *env )->ReleaseStringUTFChars( env , s , utS );
   ( *env )->ReleaseStringUTFChars( env , p , utP );
   ( *env )->ReleaseStringUTFChars( env , r , utR );

   return ( *env )->NewStringUTF( env , sub );
}


/************************************************************************
*   JNI Called function
*      Lua Exported Function
************************************************************************/

JNIEXPORT jstring JNICALL Java_org_keplerproject_luajava_LuaState__1LfindTable
  (JNIEnv * env , jobject jobj , jobject cptr , jint idx , jstring fname , jint szhint)
{
   lua_State * L   = getStateFromCPtr( env , cptr );
   const char * name = ( *env )->GetStringUTFChars( env , fname , NULL );

   const char * sub = luaL_findtable( L , ( int ) idx , name , ( int ) szhint );

   ( *env )->ReleaseStringUTFChars( env , fname , name );

   return ( *env )->NewStringUTF( env , sub );
}
